﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NAnt.SVN.Tasks;
using NAnt.Core;
using NAnt.SVN.Resources;
using System.Diagnostics;

namespace NAnt.SVN.Process
{
    /// <summary>
    /// Internal abstract class to run a process
    /// $Id$
    /// </summary>
    public abstract class AbstractProcessRunner
    {
        #region Private Fields

        private bool hasrun = false;

        #endregion

        #region Public fields

        /// <summary>
        /// Check if a process has run
        /// </summary>
        public bool HasRun
        {
            get { return hasrun; }
        }

        /// <summary>
        /// This delegate is called
        /// </summary>
        /// <param name="e"></param>
        public delegate void OnHasRunEventHandler(ProcessRunnerData e);

        /// <summary>
        /// This ivent is called after a process has run
        /// </summary>
        public event OnHasRunEventHandler OnHasRun;

        #endregion

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="task"></param>
        public AbstractProcessRunner()
        {
        }

        #region Run functions

        /// <summary>
        /// Execute the process
        /// </summary>
        public void Execute(SVNTask task)
        {
            Execute(CreateArgumentsFromTask(task));
        }

        /// <summary>
        /// Execute the process
        /// </summary>
        /// <param name="task"></param>
        /// <param name="options"></param>
        public void Execute(SVNTask task, string options)
        {
            ProcessRunnerArguments _args = CreateArgumentsFromTask(task);
            _args.Options = options;
            Execute(_args);
        }

        /// <summary>
        /// Execute the process
        /// </summary>
        public void Execute(ProcessRunnerArguments e)
        {
            //Reset flag
            hasrun = false;
            //Prepare the execution
            OnExecutePrepare(e);
            //Log the process
            try
            {
                //Execute the command
                var data = OnExecute(e);
                //Call the event handler
                if (OnHasRun != null)
                {
                    OnHasRun(data);
                }
            }
            catch (Exception _ex)
            {
                //Do something on fail
                OnExecuteFail(_ex);
                throw new BuildException(string.Format(getString("SVN0303"), e.Command, _ex, _ex.Message), 
                    e.Task.GetLocation());
            }
            finally
            {
                //Finalize the run
                OnExecuteFinalize();
                hasrun = true;
            }
        }

        #endregion

        #region Abstract Methods

        /// <summary>
        /// Prepare execution. This method is called before the try catch block
        /// </summary>
        protected abstract void OnExecutePrepare(ProcessRunnerArguments e);

        /// <summary>
        /// This method is the actual execution body, called within the try catch block
        /// </summary>
        protected abstract ProcessRunnerData OnExecute(ProcessRunnerArguments e);

        /// <summary>
        /// This method is called within the catch block. The abstract class already
        /// provides an exeption throw. If you want to override it, throw it in this 
        /// class.
        /// </summary>
        protected abstract void OnExecuteFail(Exception e);

        /// <summary>
        /// This method is run in the finalize block
        /// </summary>
        protected abstract void OnExecuteFinalize();

        #endregion

        #region Util Methods

        /// <summary>
        /// Internal function to get a string
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        protected string getString(string key)
        {
            return Strings.ResourceManager.GetString(key);
        }

        /// <summary>
        /// Create a ProcessRunnerArguments object from a task
        /// </summary>
        /// <param name="task"></param>
        /// <returns></returns>
        public static ProcessRunnerArguments CreateArgumentsFromTask(SVNTask task)
        {
            return new ProcessRunnerArguments() { 
                Task = task,
                Command = task.Command,
                Options = task.FormattedOptions
            };
        }

        #endregion
    }

    /// <summary>
    /// Process runner arguments
    /// $Id$
    /// </summary>
    public struct ProcessRunnerArguments
    {
        /// <summary>
        /// The command string
        /// </summary>
        public string Command { get; set; }
        
        /// <summary>
        /// The command options
        /// </summary>
        public string Options { get; set; }

        /// <summary>
        /// The task object
        /// </summary>
        public SVNTask Task { get; set; }
    }
}